/*
 * @Copyright: 2016 www.yyfax.com Inc. All rights reserved.
 */
package com.yyfax.pay.order.controller.api;

import javax.validation.Valid;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.yyfax.commons.lang.IdCardValidateUtil;
import com.yyfax.commons.lang.ValidateUtil;
import com.yyfax.framework.exception.YYException;
import com.yyfax.framework.model.YYFaxRsp;
import com.yyfax.framework.yyfax.CryptUtil;
import com.yyfax.framework.yyfax.enums.RspCodeEnum;
import com.yyfax.pay.common.annotation.CheckSign;
import com.yyfax.pay.common.constants.PayConstants;
import com.yyfax.pay.common.enums.ApiTypeEnum;
import com.yyfax.pay.common.enums.PayModeEnum;
import com.yyfax.pay.common.enums.PayRspCodeEnum;
import com.yyfax.pay.common.enums.PaymentEnum;
import com.yyfax.pay.common.enums.QueryTypeEnum;
import com.yyfax.pay.common.exception.YYPayException;
import com.yyfax.pay.common.model.to.YYPayApiReq2;
import com.yyfax.pay.common.util.RequestUtil;
import com.yyfax.pay.order.data.service.PayOrderService;
import com.yyfax.pay.order.data.service.UserAuthService;
import com.yyfax.pay.order.model.po.PayOrderPO;
import com.yyfax.pay.order.model.to.CounterSignParams;
import com.yyfax.pay.order.model.to.QueryPayOrderContent;
import com.yyfax.pay.order.model.to.QueryPayOrderParams;
import com.yyfax.pay.order.model.to.VerifyCodeContent;
import com.yyfax.pay.order.model.to.WebFormConfirmParams;
import com.yyfax.pay.order.model.to.pay.PayPurchaseApiContent;
import com.yyfax.pay.order.model.to.pay.PayPurchaseDirectContent;
import com.yyfax.pay.order.model.to.pay.PayPurchaseDirectParams;
import com.yyfax.pay.order.model.to.pay.PaySendVerifyDirectParams;
import com.yyfax.pay.order.model.to.pay.PayVerifyDirectContent;
import com.yyfax.pay.order.model.vo.PayOrderVO;
import com.yyfax.pay.order.model.vo.UserAuthVO;
import com.yyfax.pay.order.model.vo.UserDefaultAuthenticationVO;
import com.yyfax.pay.order.model.vo.UserPaymentVO;
import com.yyfax.pay.order.service.PayCommonService;
import com.yyfax.pay.order.service.PayService;
import com.yyfax.pay.order.service.RouteService;
import com.yyfax.pay.third.model.to.yyfax.PayOrderYYContent;
import com.yyfax.pay.third.model.to.yyfax.QueryCardBinContent;
import com.yyfax.pay.third.model.to.yyfax.SendVerifyCodeYYContent;
import com.yyfax.pay.third.model.to.yyfax.WebFormYYContent;

/**
 * 支付下单 --> api入口
 * 
 * @author buyi
 * @date 2017-04-13 21:43:02
 * @since v1.0.0
 */
@RestController
public class PayApiController {
	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private PayService payService;

	// @Autowired
	// private PayoutService payoutService;

	@Autowired
	private PayOrderService payOrderService;

	// @Autowired
	// private MerchantConfigService merchantConfigService;

	@Autowired
	private UserAuthService userAuthService;

	@Autowired
	private PayCommonService payCommonService;

	@Autowired
	private RouteService routeService;

	@Autowired
	private PayConstants payConstants;

	/**
	 * 三要素表单页面响应请求
	 * 
	 * @author buyi
	 * @date 2017-04-13 11:39:53
	 * @since v1.0.0
	 * @param payId 支付订单自增主键
	 * @param merchantCode 商户编号
	 * @param sign 签名串
	 * @param idNo 身份证号
	 * @param name 姓名
	 * @param cardNo 银行卡号
	 * @return
	 * @throws Throwable
	 */
	@SuppressWarnings("rawtypes")
	@RequestMapping({ "/api/pay/real_name/confirm" })
	public YYFaxRsp<WebFormYYContent> realNameConfirm(WebFormConfirmParams params) throws Throwable {
		// 1.校验参数
		params.validate();

		// 校验三要素
		if (StringUtils.isBlank(params.getFormCardNo()) || StringUtils.isBlank(params.getFormIdNo()) || StringUtils.isBlank(params.getFormName())) {
			throw new YYException(RspCodeEnum.COMMON_ERROR_PARAMETER_INVALID, "银行卡号/证件号码/姓名不能为空");
		} else { // 校验合法性

			// 身份证号
			if (!IdCardValidateUtil.isIdCard(params.getFormIdNo())) {
				throw new YYException(RspCodeEnum.COMMON_ERROR_PARAMETER_INVALID, "idNo非法", "身份证号非法");
			}

			if (params.getFormName().length() > PayConstants.NAME_MAX_LENGTH) {
				throw new YYPayException(RspCodeEnum.COMMON_ERROR_PARAMETER_INVALID, "name非法", "姓名不能超过最大长度" + PayConstants.NAME_MAX_LENGTH);
			}

			// 校验姓名
			if (!ValidateUtil.isName(params.getFormName())) {
				throw new YYException(RspCodeEnum.COMMON_ERROR_PARAMETER_INVALID, "name姓名非法，只支持中文姓名", "姓名非法，只支持中文姓名");
			}
		}

		// 2. 签名校验
		params.checkSign(payConstants.getSignKey());

		params.setPayMode(PayModeEnum.AUTH_PAY.getValue());

		// 如果实名认证页面银行卡号、姓名、身份证号，有优先传参
		if (StringUtils.isBlank(params.getCardNo())) {
			params.setCardNo(params.getFormCardNo().trim());
		}

		if (StringUtils.isBlank(params.getIdNo())) {
			params.setIdNo(params.getFormIdNo().trim());
		}

		if (StringUtils.isBlank(params.getName())) {
			params.setName(params.getFormName().trim());
		}

		WebFormYYContent<?> form = payService.getWebForm(params);

		// 3.返回请求数据封装
		return new YYFaxRsp<WebFormYYContent>(PayRspCodeEnum.SUCCESS, form);
	}

	/**
	 * 支付模式选择页面响应请求
	 * 
	 * @author buyi
	 * @date 2017-04-19 10:53:30
	 * @since v1.0.0
	 * @param params
	 * @return
	 * @throws Throwable
	 */
	@SuppressWarnings("rawtypes")
	@RequestMapping({ "/api/pay/select/confirm" })
	public YYFaxRsp<WebFormYYContent> selectConfirm(WebFormConfirmParams params) throws Throwable {
		// 1.校验参数
		params.validate();

		// 2. 签名校验
		params.checkSign(payConstants.getSignKey());

		// 3.读取鉴权信息
		PayOrderPO payOrderPO = payOrderService.queryByPayOrderId(params.getMerchantCode(), params.getPayOrderId());
		if (payOrderPO == null) {
			throw new YYException(PayRspCodeEnum.COMMON_ERROR_OBJECT_NOT_FOUND, "订单不存在:" + params.getPayOrderId(), "订单不存在:" + params.getPayOrderId());
		}

		UserAuthVO userAuthVO = userAuthService.queryUserAuthVO(params.getMerchantCode(), payOrderPO.getUserId());
		if (userAuthVO == null || userAuthVO.getUserPaymentVOs().isEmpty()) {
			throw new YYException(PayRspCodeEnum.COMMON_ERROR_OBJECT_NOT_FOUND, "鉴权信息不存在", "鉴权信息不存在");
		}

		// 如果商户下单过程，么有优先传银行卡、姓名、身份证号，则通过路由取有效的用户支付认证信息
		if (StringUtils.isBlank(params.getCardNo())) {
			// UserPaymentPO userPaymentPO = userAuthVO.getUserPaymentPOs().get(0);
			UserDefaultAuthenticationVO userDefaulAuthenticationInfo = payService.getUserDefaulAuthenticationInfo(payOrderPO.getMerchantCode(),
					payOrderPO.getUserId());
			if (userDefaulAuthenticationInfo == null || userDefaulAuthenticationInfo.getUserPaymentVO() == null) {
				throw new YYPayException(PayRspCodeEnum.COMMON_ERROR_OBJECT_NOT_FOUND, "鉴权信息不存在");
			}

			UserPaymentVO userPaymentVO = userDefaulAuthenticationInfo.getUserPaymentVO();

			params.setCardNo(CryptUtil.decrypt(userPaymentVO.getCardNoCip()));
			params.setIdNo(CryptUtil.decrypt(userPaymentVO.getIdNoCip()));
			params.setName(userPaymentVO.getName());
		}

		WebFormYYContent<?> form = payService.getWebForm(params);

		// 3.返回请求数据封装
		return new YYFaxRsp<WebFormYYContent>(PayRspCodeEnum.SUCCESS, form);
	}

	/**
	 * 发送支付短信验证码
	 * 
	 * @author buyi
	 * @date 2017-04-13 21:45:43
	 * @since v1.0.0
	 * @param merchantCode
	 * @param cardNo
	 * @return
	 * @throws Throwable
	 */
	@RequestMapping(method = RequestMethod.POST, path = "/api/pay/send_verify")
	public YYFaxRsp<VerifyCodeContent> sendVerifyCode(CounterSignParams params) throws Throwable {

		// 1.参数校验
		params.validate();

		// 2.验签
		params.checkSign(payConstants.getSignKey());

		// 获取订单信息
		PayOrderVO payOrderVO = payOrderService.queryPayOrderVO(params.getPayOrderId(), params.getMerchantCode());
		if (payOrderVO == null || payOrderVO.getPayOrderExtPO() == null) {
			throw new YYPayException(PayRspCodeEnum.COMMON_ERROR_OBJECT_NOT_FOUND, "订单：" + params.getPayOrderId() + "不存在", "无效订单");
		}

		if (payOrderVO.isExpire()) {
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_ORDER_IS_CLOSE, "订单：" + params.getPayOrderId() + "已经失效", "订单超时，请重新购买");
		}

		if (payOrderVO.isClose()) { // 如果订单状态是已经关闭，或者过期
			logger.info("订单：{}，已经失效，isClose:{},isExpire:{}", params.getPayOrderId(), payOrderVO.isClose(), payOrderVO.isExpire());
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_ORDER_IS_CLOSE, "订单：" + params.getPayOrderId() + "已经关闭/失效", "订单已关闭");
		}

		SendVerifyCodeYYContent sendVerifyCodeYYContent = payService.sendVerifyCode(payOrderVO, params.getPhone());
		VerifyCodeContent content = new VerifyCodeContent(sendVerifyCodeYYContent.getToken(), sendVerifyCodeYYContent.getSerialNo());

		// 生成表单重复提交token
		content.setFormToken(payCommonService.generateDuplicateToken(params.getPayOrderId()));

		return new YYFaxRsp<VerifyCodeContent>(sendVerifyCodeYYContent.getResultCode(), sendVerifyCodeYYContent.getResultMsg(),
				sendVerifyCodeYYContent.getResultMsg(), content);
	}

	/**
	 * 发送支付请求
	 * 
	 * @author buyi
	 * @date 2017-04-27 10:12:29
	 * @since v1.0.0
	 * @param params
	 * @return
	 * @throws Throwable
	 */
	@RequestMapping(method = RequestMethod.POST, path = "/api/pay/purchase")
	public YYFaxRsp<PayPurchaseApiContent> purchase(CounterSignParams params) throws Throwable {
		// 1.参数校验
		params.validate();

		// 2.验签
		params.checkSign(payConstants.getSignKey());

		// 验证表单重复提交
		payCommonService.checkAndDelDuplicateToken(params.getPayOrderId(), params.getFormToken());

		// 获取订单信息
		PayOrderVO payOrderVO = payOrderService.queryPayOrderVO(params.getPayOrderId(), params.getMerchantCode());
		PayOrderYYContent payOrderYYContent = payService.validatePayOrderForPurchase(payOrderVO);
		if (payOrderYYContent == null) {
			payOrderYYContent = payService.payOrder(payOrderVO, params.getPhone(), params.getToken(), params.getValidCode(), params.getSerialNo());
		}

		// 封装回显参数
		PayPurchaseApiContent payPurchaseApiContent = payService.transformPayReturnUrl(payOrderYYContent.getResultCode(),
				payOrderYYContent.getResultMsg(), params.getMerchantCode(), params.getPayOrderId());

		// 重新生成表单令牌
		payPurchaseApiContent.setFormToken(payCommonService.generateDuplicateToken(params.getPayOrderId()));

		// 响应码直接取商户返回
		// return new YYFaxRsp<PayPurchaseApiContent>(PayRspCodeEnum.fromValue(payOrderYYContent.getResultCode()), payOrderYYContent.getResultMsg(),
		// payPurchaseApiContent);
		return new YYFaxRsp<PayPurchaseApiContent>(payOrderYYContent.getResultCode(), payOrderYYContent.getResultMsg(),
				payOrderYYContent.getResultMsg(), payPurchaseApiContent);
	}

	/**
	 * 查询订单状态
	 * 
	 * @author buyi
	 * @date 2017-04-18 18:00:28
	 * @since v1.0.0
	 * @param params
	 * @return
	 * @throws Throwable
	 */
	@CheckSign
	@RequestMapping(method = RequestMethod.POST, path = "/api/pay/query")
	public YYFaxRsp<QueryPayOrderContent> payOderQuery(@RequestBody @Valid YYPayApiReq2<QueryPayOrderParams> req) throws Throwable {
		logger.info("进入支付查询，merchantCode:{},orderId:{}", req.getMerchantCode(), req.getParams().getOrderId());

		QueryPayOrderParams params = req.getParams();

		QueryPayOrderContent content = payService.queryPayOrderResult(params.getOrderId(), req.getMerchantCode(), QueryTypeEnum.INITIATIVE);

		return new YYFaxRsp<QueryPayOrderContent>(PayRspCodeEnum.SUCCESS, content);
	}

	/**
	 * 直连 —— 支付下单（发送验证码）
	 * 
	 * @author buyi
	 * @date 2017-04-27 09:44:04
	 * @since v1.0.0
	 * @return
	 * @throws Throwable
	 */
	@RequestMapping(method = RequestMethod.POST, path = "/api/pay/direct/send_verify")
	@CheckSign
	public YYFaxRsp<PayVerifyDirectContent> directSendVerifyCode(@RequestBody @Valid YYPayApiReq2<PaySendVerifyDirectParams> req) throws Throwable {
		logger.debug("enter payOrder for api controller");

		PaySendVerifyDirectParams params = req.getParams();

		// 获取真实IP地址
		RequestAttributes ra = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes sra = (ServletRequestAttributes) ra;
		params.setServerIp(RequestUtil.getIpAdr(sra.getRequest()));

		// 1.校验参数
		params.validate();

		// 2.验签
		String idNo = null;
		String name = null;
		String cardNo = null;
		String bankCode = null;
		String bankName = null;

		UserDefaultAuthenticationVO userDefaulAuthenticationInfo = payService.getUserDefaulAuthenticationInfo(params.getMerchantCode(),
				params.getUserId());

		if (userDefaulAuthenticationInfo == null || userDefaulAuthenticationInfo.getUserPaymentVO() == null) { // 如果支付通道鉴权为空
			// 校验首次支付信息
			params.validateFirst();

			QueryCardBinContent cardBin = payCommonService.queryCardBin(params.getMerchantCode(), params.getCardNo());
			bankCode = cardBin.getBankCode();
			bankName = cardBin.getBankName();

			idNo = params.getIdNo();
			name = params.getName();
			cardNo = params.getCardNo();
		} else {

			UserPaymentVO userPaymentVO = userDefaulAuthenticationInfo.getUserPaymentVO();
			bankCode = userPaymentVO.getBankCode();
			bankName = userPaymentVO.getBankName();

			idNo = CryptUtil.decrypt(userPaymentVO.getIdNoCip());
			name = userPaymentVO.getName();
			cardNo = CryptUtil.decrypt(userPaymentVO.getCardNoCip());
		}

		// 获取相应支付渠道
		PaymentEnum paymentEnum = routeService.routePay(params.getMerchantCode(), bankCode, params.getPaymentCode(), PayModeEnum.AUTH_PAY.getValue(),
				ApiTypeEnum.API.getValue());

		if (!PaymentEnum.FUIOU.equals(paymentEnum) && !PaymentEnum.QUICK_PAY.equals(paymentEnum) && !paymentEnum.isValue(params.getPaymentCode())) {
			logger.info("直连发送短信验证只支持快钱，富友，且支付路由必须为偏好支付通道", params.getPaymentCode(), paymentEnum.getValue());
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAYMENT_CODE_NOT_HIT, "该商户不支持的支付通道");
		}

		// 订单持久化
		PayOrderVO payOrderVO = payService.savePayOrder(params, paymentEnum, PayModeEnum.AUTH_PAY, ApiTypeEnum.API, idNo, name, cardNo, bankCode,
				bankName);

		// 发送验证码
		SendVerifyCodeYYContent sendVerifyCodeYYContent = payService.sendVerifyCode(payOrderVO, params.getPhone());

		PayVerifyDirectContent content = new PayVerifyDirectContent(sendVerifyCodeYYContent.getToken(), sendVerifyCodeYYContent.getSerialNo());

		return new YYFaxRsp<PayVerifyDirectContent>(sendVerifyCodeYYContent.getResultCode(), sendVerifyCodeYYContent.getResultMsg(),
				sendVerifyCodeYYContent.getResultMsg(), content);
	}

	/**
	 * 直连 —— 发起支付
	 * 
	 * @author buyi
	 * @date 2017-04-27 19:13:21
	 * @since v1.0.0
	 * @param req
	 * @return
	 * @throws Throwable
	 */
	@CheckSign
	@RequestMapping(method = RequestMethod.POST, path = "/api/pay/direct/purchase")
	public YYFaxRsp<PayPurchaseDirectContent> directPurchase(@RequestBody @Valid YYPayApiReq2<PayPurchaseDirectParams> req) throws Throwable {

		if (req == null || req.getParams() == null) {
			throw new YYException(RspCodeEnum.COMMON_ERROR_PARAMETER_INVALID);
		}

		PayPurchaseDirectParams params = req.getParams();

		// 1.校验参数
		params.validate();

		// 2.验签
		// MerchantConfigPO merchantConfigPO = merchantConfigService.queryByMerchantCode(params.getMerchantCode());
		// if (merchantConfigPO == null || !YesNoEnum.YES.isValue(merchantConfigPO.getState())) {
		// throw new YYPayException(PayRspCodeEnum.PAY_CODE_MERCHANT_NOT_EXISTS, "不存在[" + params.getMerchantCode() + "]商户信息，或者状态无效");
		// }
		//
		// req.checkSign(merchantConfigPO.getSignKey());

		// 获取订单信息
		PayOrderVO payOrderVO = payOrderService.queryPayOrderVOByOrderId(params.getOrderId(), params.getMerchantCode());
		PayOrderYYContent payOrderYYContent = payService.validatePayOrderForPurchase(payOrderVO);
		if (payOrderYYContent == null) {
			payOrderYYContent = payService.payOrder(payOrderVO, params.getPhone(), params.getToken(), params.getValidCode(), params.getSerialNo());
		}

		PayPurchaseDirectContent content = new PayPurchaseDirectContent();
		content.setMerchantCode(payOrderVO.getMerchantCode());
		content.setOrderId(payOrderVO.getOrderId());
		content.setSerialNo(payOrderYYContent.getSerialNo());

		return new YYFaxRsp<PayPurchaseDirectContent>(payOrderYYContent.getResultCode(), payOrderYYContent.getResultMsg(),
				payOrderYYContent.getResultMsg(), content);
	}
}
